#include "command.h"

#include <expected>
#include <format>
#include <iostream>
#include <filesystem>
#include <ranges>

#include "utility.h"

command::command(const std::string &input) : raw_input{input} {
	if(input.contains(' ')) {
		auto first_space = input.find_first_of(' ');
		command_ = input.substr(0, first_space);
		type = string_to_commands[command_];
		args_ = split<std::string>(input.substr(first_space+1), " ");
	} else {
		command_ = input;
		type = string_to_commands[command_];
	}
}
enum path_error {
	EXECUTABLE_NOT_FOUND,
	PATH_NOT_FOUND
};

std::expected<std::string, path_error> process_global_environment_path(const std::string& command) {
	std::string str{std::getenv("PATH")};
	for(const auto& path : split<std::filesystem::path>(str, ":")) {
		if(std::filesystem::exists(path/command)) {
			return (path/command).string();
		}
	}
	return std::unexpected(path_error::EXECUTABLE_NOT_FOUND);
}

std::expected<std::filesystem::path, path_error> process_path(const std::string& path) {
	if(std::filesystem::exists(path))
		return path;
	return std::unexpected(path_error::PATH_NOT_FOUND);
}

std::string process_TYPE(const std::string& command) {
	if(string_to_commands.contains(command))
		return std::format("{} is a shell builtin\n", command);

	auto find_exec = process_global_environment_path(command);
	if(find_exec)
		return std::format("{} is {}\n", command,find_exec.value());
	return std::format("{}: not found\n", command);

}

std::string process_ECHO(const std::vector<std::string>& args) {
	std::stringstream result;
	for(std::size_t i = 0; i < args.size(); i++) {
		result << args[i];
		if(i != args.size()-1)
			result << " ";
	}
	result << "\n";
	return result.str();
}


std::tuple<int, std::string> process_COMMAND_FROM_PATH(const std::string& input) {
	std::string result;
	std::array<char, 256> buffer{};
	int rc;
	std::string command = input + + " 2>&1";
	auto pipe = popen(command.c_str(), "r");
	while (std::fgets(buffer.data(), static_cast<int>(buffer.size()), pipe) != nullptr) {
		result += buffer.data();
	}
	rc = pclose(pipe);
	if(WIFEXITED(rc)) {
		rc = WEXITSTATUS(rc);
	}

	return std::make_tuple(rc, result);
}

std::string proccess_PWD() {
	return std::filesystem::current_path().string()+"\n";
}

std::tuple<int, std::string> process_CD(const std::string& path) {
	if(path == "~") {
		std::filesystem::current_path(getenv("HOME"));
		return {0, ""};
	}
	if(not process_path(path)) {
		return {-1, std::format("cd: {}: No such file or directory\n", path)};
	}
	std::filesystem::current_path(path);
	return {0, ""};
}


std::tuple<int, std::string> command::run() {
	if(commands_enum::COMMAND_NOT_FOUND == type)
		process_command();
	switch(type) {
		case commands_enum::COMMAND_NOT_FOUND:
			return {-1, std::format("{}: command not found\n", command_)};
		case commands_enum::EXIT:
			if(args_.empty())
				return {0,""};
			return {std::stoi(args_[0]),""};
		case commands_enum::ECHO:
			return {0, process_ECHO(args_)};
		case commands_enum::TYPE:
			return {0, process_TYPE(args_[0])};
		case commands_enum::COMMAND_FROM_PATH:
			return process_COMMAND_FROM_PATH(raw_input);
		case commands_enum::PWD:
			return {0, proccess_PWD()};
		case commands_enum::CD:
			return process_CD(args_[0]);
	}
	return {-1, "Error ocured"};
}

void command::process_command() {
	auto val = process_global_environment_path(command_);
	if(val) {
		command_ = val.value();
		type = commands_enum::COMMAND_FROM_PATH;
	}
}

